package com.i72.basic.tools;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;

public class BeanHelper {


    //private final static Map<String,List<Annotation>> GETANNOTATION_CACHE = new HashMap<>();
    //private final static Map<String,List<Annotation>> FINDANNOTATION_CACHE = new HashMap<>();
    private final static Map<String,List<Method>> GETMETHOD_CACHE = new HashMap<>();
    private final static Map<String,List<Field>> GETFIELD_CACHE = new HashMap<>();
    //private final static Map<String,List<Field>> FINDFIELD_CACHE = new HashMap<>();
    /*
    * 可以使用spring AnnotationUtils 或AnnotatedElementUtils
    * 但是我还是要自己写一个，只是为了学一下java 的反射。
    *
    * */


    public static List<Annotation> getAnnotation(Class c){
        String name = c.getName();
        Object o = null;
        //if(!GETANNOTATION_CACHE.containsKey(name)) {
            List<Annotation> annotations = Arrays.asList(c.getAnnotations());
             //GETANNOTATION_CACHE.put(name,annotations);
        //}
        //return GETANNOTATION_CACHE.get(name);
        return  annotations;
    }

    public static List<Annotation> getAnnotation(Object o){
        return getAnnotation(o.getClass());
    }

    public static List<Annotation> findAnnotation(Object o,Class c){
        return findAnnotation(o.getClass(),c);
    }

    public static List<Annotation> findAnnotation(Class c,Class a){

        //String name = c.getName()+"_"+a.getName();

        //if(!FINDANNOTATION_CACHE.containsKey(name)) {
            List<Annotation> annotations = getAnnotation(c);

            List<Annotation> findAnnotationList = new ArrayList<>();
            if (annotations != null && annotations.size() > 0) {
                for (Annotation an : annotations) {
                    Class[] classList = an.getClass().getInterfaces();

                    if (classList != null && classList.length > 0) {
                        if (classList[0].getName().equals(a.getName())) {
                            findAnnotationList.add(an);
                        }
                    }
                }
            }
           //FINDANNOTATION_CACHE.put(name,findAnnotationList);
        //}
        //return FINDANNOTATION_CACHE.get(name);
        return findAnnotationList;
    }

    public static Map<String,List<Object>> getAnnotationValue(Annotation annotation){

        Map<String,List<Object>> value = new HashMap<>();
        Method [] methods = annotation.getClass().getDeclaredMethods();
        if(methods!=null && methods.length>0) {

            for (Method mm : methods) {
                List<Object> objects = null;
                if(!value.containsKey(mm.getName())){
                    objects = new ArrayList<>();
                    value.put(mm.getName(),objects);
                }
                objects = value.get(mm.getName());

                try {
                    Object v = mm.invoke(annotation, null);
                    objects.add(v);
                } catch (Exception e) {

                }

            }



        }
        return value;
    }

    /**
     *
     * @param o
     * @param c
     * @return
     */
    public static Map<String,List<Object>> getAnnotationValue(Object o, Class c){
        /**
         * class
         *      field
         *           [value,value,value]
         */
        List<Annotation> annotations = findAnnotation(o,c);

        Method [] ms = c.getDeclaredMethods();

        Map<String,List<Object>> value = null;

        if(annotations!=null && annotations.size()>0 && ms!=null && ms.length>0){
            value = new HashMap<>();
            for(Annotation annotation:annotations){
                Method [] methods = annotation.getClass().getDeclaredMethods();
                if(methods!=null && methods.length>0) {
                    //双层循环，只为得到所有注解的值。
                    for (Method m : ms) {
                        if(!value.containsKey(m.getName())){
                            value.put(m.getName(),new ArrayList<>());
                        }

                        List<Object> objects = value.get(m.getName());

                        for (Method mm : methods) {
                            if (mm.getName().equals(m.getName())) {
                                try {
                                    Object v = mm.invoke(annotation, null);
                                    objects.add(v);
                                } catch (Exception e) {

                                }
                            }
                        }
                    }


                }


            }

        }
        return value;
    }

    public static boolean existsAnnotation(Object o,Class a){
        return existsAnnotation(o.getClass(),a);
    }

    public static boolean existsAnnotation(Class c,Class a){
        List<Annotation> annotations = findAnnotation(c,a);
        if(annotations!=null && annotations.size()>0){
            return true;
        }
        return false;
    }


    public static List<Method> getMethod(Object o){
        return getMethod(o.getClass());
    }
    public static List<Method> getMethod(Class c){
        String name = c.getName();
        if(!GETMETHOD_CACHE.containsKey(name)){
            Method [] methods = c.getDeclaredMethods();
            if(methods!=null && methods.length>0){
                List<Method> methodList = Arrays.asList(methods);
                GETMETHOD_CACHE.put(name,methodList);
            }
        }
        return GETMETHOD_CACHE.get(name);
    }

    public static List<Field> getField(Object o){
        return getField(o.getClass());
    }

    public static List<Field> getField(Class c){
        String name = c.getName();
        if(!GETFIELD_CACHE.containsKey(name)){
            Field [] fields = c.getDeclaredFields();
            if(fields!=null && fields.length>0){
                List<Field> fieldList = Arrays.asList(fields);
                GETFIELD_CACHE.put(name,fieldList);
            }
        }
        return GETFIELD_CACHE.get(name);
    }

    public static List<Field> findField(Object o,Class a){
        return findField(o.getClass(),a);
    }
    public static List<Field> findField(Class c,Class a){
        //String name = c.getName()+"_"+a.getName();

        //if(!FINDFIELD_CACHE.containsKey(name)){
            List<Field> fieldList = getField(c);
            List<Field> findFieldList= new ArrayList<>();
            if(fieldList!=null && fieldList.size()>0){
                for(Field f:fieldList){
                    Annotation [] annotations = f.getAnnotations();

                    if(annotations!=null && annotations.length>0){

                        for(Annotation an:annotations){
                            Class[] classList = an.getClass().getInterfaces();
                            if (classList[0].getName().equals(a.getName())) {
                                findFieldList.add(f);
                                //找到后立即跳出
                                break;
                            }
                        }
                    }
                }
                //FINDFIELD_CACHE.put(name,findFieldList);
            }
        //}
        //return FINDFIELD_CACHE.get(name);
        return findFieldList;
    }


    public static List<Class> scanPackage(String... packages){
        return scanPackage(null,packages);
    }

    public static List<Class> scanPackage(Class annotation, String... packages){

        List<Class> classList = new ArrayList<>();

        SimpleMetadataReaderFactory readerFactory = new SimpleMetadataReaderFactory();

        PathMatchingResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();

        Resource[] resources = null;
        List<Resource> resourceList = new ArrayList<>();
        for(String p:packages) {
            String path = p.replace(".","/");
            String resourcePath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX+path;

            if(!resourcePath.endsWith("/")){
                resourcePath+="/";
            }

            resourcePath +="**/*.class";
            try {

                resources = resourcePatternResolver.getResources(resourcePath);
                if(resources!=null && resources.length>0) {
                    resourceList.addAll(Arrays.asList(resources));
                }
            }catch (Exception e){

            }
        }

        String className = "";
        if(resourceList!=null && resourceList.size()>0) {
            for (Resource r : resourceList){
                try {
                    MetadataReader metadataReader = readerFactory.getMetadataReader(r);

                    AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
                    ClassMetadata classMetadata = metadataReader.getClassMetadata();

                    if(annotation!=null){

                        if(annotationMetadata.getAnnotationTypes().contains(Service.class.getName())){
                            className = classMetadata.getClassName();
                        }else{
                            className = "";
                        }
                    }else{
                        className = classMetadata.getClassName();
                    }

                    if(!StringUtils.isEmpty(className)) {
                        classList.add(Class.forName(className));
                    }

                }catch (Exception e){

                }
            }
        }

        return classList;
    }

    public static void registerBean(BeanFactory factory,boolean lazy,boolean primary,List<Class> classes){

        if(classes!=null && classes.size()>0){
            for(Class c:classes){
                registerBean(factory,"","",lazy,primary,c);
            }
        }
    }

    public static void registerBean(BeanFactory factory,String scope,String initMethodName,boolean lazy,boolean primary,Class c) {

        DefaultListableBeanFactory f = (DefaultListableBeanFactory) factory;

        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(c);
        GenericBeanDefinition definition = (GenericBeanDefinition) builder.getRawBeanDefinition();
        definition.setBeanClass(c);
        definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
        if(StringUtils.isEmpty(scope)) {
            definition.setScope(GenericBeanDefinition.SCOPE_SINGLETON);
        }else{
            definition.setScope(scope);
        }
        if(!StringUtils.isEmpty(initMethodName)) {
            definition.setInitMethodName(initMethodName); //手动设置初始化方法
        }

        definition.setPrimary(primary);

        definition.setLazyInit(lazy);
        f.registerBeanDefinition(c.getName(), definition);

    }


    /**
     * 查找一个类型的父类型（有接口则是第一个接口，然后在是父类。啥都没有则为本身（去除顶层Object条件））
     * @param c
     * @return
     */
    public static Class getSuperClass(Class c){
        if(c!=null){

            Class<?>[] interfaces = c.getInterfaces();
            if(interfaces!=null && interfaces.length>0){
                return interfaces[0];
            }

            Class classType = c.getSuperclass();

            if(!classType.getName().equals(Object.class.getName())){
                return classType;
            }
            return c;

        }
        return null;
    }
}
